別懷疑,複製就是拷貝,深拷貝淺拷貝就是深複製淺複製,呼~
人家說,如果我們可以和某位朋友一起旅行 30 天,要嘛旅程結束變至親,不然就是變仇人,鐵人賽已經第十一天,本人也開始有點了解 Array 這位木訥寡言,但一肚子學問的中年大叔。很難想像,光陣列就可以讓我寫十天寫不盡,即使寫完它,但是後頭還有多少 JavaScript 特性或其他奇怪的東西要了解?不過,只要確定走在進步的路上,那就繼續走吧!
要說到陣列的複製,得讓我們聊聊 Javascript 是怎麼將型別分類的,原因是這牽涉到記憶體的分配與讀取的方式。在這之前,我們再來複習一下,在前幾天有聊到的 Javascript 資料型別。
在「你說不知道的 JS 導讀,型別與文法」提面提到,JavaScript 定義了七個內建型別:
而在這些型別中,又被分為兩大類Primitive type(基本型別)和Non-primitive type(非基本型別)。形容這兩大類的詞彙有許多版本,我們可以透過底下這張圖看得更清楚一點。
實作時常會聽到call by reference
還是 call by value
,這兩個有什麼不同?最簡易的解釋方法是:
Objet
和 Array
在 Javascript 屬於複合型(composite)或參考型(reference)的原始資料類型,在呼叫、複製或傳參數的時候,是參考記憶體的位址而不是值,這點要特別注意。
在查找資料時,又多了新的疑問,因為許多人談到另一種名叫call by sharing
的呼叫方式,雖然讀了許多資料,但本人還是無法清楚的釐清它和Call by reference
的相異之處,有興趣的朋友可以來看看這篇深入探討 JavaScript 中的參數傳遞:call by value 還是 reference? 相信會有收穫的,在本人還沒完全吸收的狀況下,還是留給高人來解說比較保險。
這些抽象的概念,初學者並不是那麼容易懂,但透過實作會讓這些抽象部分更有感覺。
我們先創建一個變數a
,再將a
複製給b
,接著重新給b
一個新值,然後以這兩個變數的結果來觀察。我們會發現a
和b
都有各自的值。a
的值不會因為b
而更動到。
// by value
let a = "something";
let b = a;
b = "somebody";
a; // something
b; // somebody
我們先以物件資料型態的物件來測試。
先建立一個物件person
,並給予兩個屬性,在另創一個變數person2
,並將person
指向(或複製)到person2
,這時我們試著修改person2
的某個屬性,結果person
的屬性也被更動了。
在修改person2
這個object
裡的name
屬性時,也會同時改到原來參考的person
的name
屬性,是不是很恐怖?
因為他們兩個是住在同一個記憶體裡啊!只不過,當我們認為從person
複製一份到person2
這件事,實際上是並沒有像影印一樣拷貝一份,而是拷貝時,我們跟person2
說,要找person2
就來person
這個位址查找吧,所以來到同樣一個位址,改變person2
也就理所當然的改到原本的person
。
// by reference(參考值)
let person = {
name: "Tracy",
city: "Tainan"
}
let person2 = person;
person2.name = "Ayda"
person; // name: "Ayda"
person2; // name: "Ayda"
接下來我們來測試我們的主角陣列。
同樣是先創建一個陣列的變數arr1
,並複製一份給arr2
,接著我們試著在arr2
的index3
位置加入一個陣列元素4
(使用push()
也可以),再把兩個陣列叫出來觀察。
嗯,arr1
也被更動到了! 是不是很討厭!
// 陣列 by reference(參考值)
let arr1 = [1,2,3];
let arr2 = arr1;
arr2[3] = 4;
arr1; // [1, 2, 3, 4]
arr2; // [1, 2, 3, 4]
因為Array
也是參考型(reference)
的原始資料類型所以也會像Object
一樣,在修改時也會更改到原始參考值by reference
。
這樣的狀況會對寫程式和處理資料有什麼影響? 會出人命啊 當然會。
想像一下我們跟同學借了筆記來抄,抄完之後我們開始在我們抄過來的筆記上畫重點,甚至塗鴉,等到借我們筆記的同學翻開他的筆記時,發現 這什麼鬼啊 筆記上有著我們畫的密密麻麻的記號和塗鴉,友誼還要走下去嗎?
上面的複製方式是我們最常用的,也是最直覺的複製方法,但是卻對陣列與物件的資料行不通,有其他方法複製嗎?當然有 或....應該有~~。
我們可以利用 JavaScript 許多內建的方法來做複製這件事,但是 JavaScript 可沒那麼容易讓我們複製物件型態的資料。大家也聽過很多人都在聊的「深拷貝(DeepCopy)」和「淺拷貝(ShallowCopy)」,這兩者的區別又在哪裡呢?
我們可以先想像一下,在 JavaScript 裡,要複製這些物件型別的「DNA」是有難度的,假如我們想徹徹底底地模仿一位巨星,髮型、服飾、化妝、配件都和這位巨星打扮的一模一樣,這樣或許看起來一樣,但當我們講話、走路時,就馬上會被識破,然後我們努力的把自己的音調也改了、姿態也修正了,以為一切都完美了,結果,遇到這會巨星的好朋友或男友,你想,我們會過關嗎?
物件型別的複製,正有如以上講述情境的尷尬,也就是說,即使是複製,也是分好幾個層級的。我們明天再來聊聊,如何改變我們的DNA
,完完全全地成為那位巨星! 甘五摳零?
如有需要改進的地方,拜託懇求請告知,我會盡量快速度修改,感謝您~
謝謝你生動的描述 讓生硬的內容變的更容易理解了
謝謝你,也很高興有幫到你理解有點小複雜的 Array 複製 :-)